//#include "d3d.h"
#include "interface_types.h"
#include "d3d_pointsprite.h"
#include "d3d_texture.h"
#include "d3d_util.h"
#include "d3d_image.h"
#include "d3d.h"
#include "..\common\util.h"
#include "..\common\def.h"
#include "..\common\common.h"
#include "..\engine\error.h"
#include "..\common\arraylist.hpp"
#include "..\math\math.h"


/**************************************
 *
 **************************************/
D3DPointSpriteGroup::D3DPointSpriteGroup(CD3D9Device* const pDevice) : D3DRenderable(pDevice)
{	
	m_Capacity = 0; 
		m_RenderFlags = RENDER_DEFAULT; 
} 

/**************************************
 *
 **************************************/
void D3DPointSpriteGroup::Flush()
{
	m_Sprites.ClearAllocatedSpace(); 
}

/**************************************
 *
 **************************************/
D3DPointSpriteGroup::~D3DPointSpriteGroup()
{
	m_RegisteredSprites.Empty(); 
	m_Sprites.Empty(); 
}


/**************************************
 *
 **************************************/
D3DSprite::D3DSprite(CD3D9Device* const pDevice) : IResource(pDevice)
{	
	m_Identity = "Sprite"; 
	 m_Index = 0; 
	 m_pGroup = NULL; 
	EE_ZEROMEMORY(&m_Point, sizeof(Point)); 
	m_Point.radius = 100.0f;

}
string D3DSprite::GetType() const
{
	return string("D3DSprite"); 
}
/**************************************
 *
 **************************************/
void D3DSprite::Draw()
{
	if(m_pGroup)
		m_pGroup->m_Sprites.Add(m_Point); 
}

HRESULT D3DSprite::Reload()
{
	//TODO:
	return S_OK; 
}

/**************************************
 *
 **************************************/
void D3DSprite::setRadius(const float &radius) 
{

	m_Point.radius = MAX(0.0f,radius); 
}

/**************************************
 *
 **************************************/
void D3DSprite::setPosition(const Vector3f &position) {
	
	m_Point.position[0] = position.x; 
	m_Point.position[1] = position.y; 
	m_Point.position[2] = position.z; 
	
}
/**************************************
 *
 **************************************/
void D3DSprite::setRenderFlags(const int flags)
{
	if(m_pGroup)
		m_pGroup->setRenderFlags(flags); 
}

/**************************************
 *
 **************************************/
void D3DSprite::setColor(const Color3f &color) {

	memcpy(m_Point.color, &color, sizeof(float) * 3); 
}

/**************************************
 *
 **************************************/
HRESULT D3DSprite::Release()
{
	m_IsValid = false; 
	if(m_pGroup)
	{
		m_pGroup->m_RegisteredSprites.Remove(this); 
		return S_OK; 
	}
	return E_FAIL; 
}

HRESULT D3DPointSpriteGroup::Reload()
{
	//TODO:
	return S_OK; 
}
string D3DPointSpriteGroup::GetType() const
{
	return string("D3DPointSpriteGroup"); 
}

D3DPointSpriteGroup::D3DPointSpriteGroup(CD3D9Device* pDevice, const string szName, D3DTexture2D* const pTexture) : D3DRenderable(pDevice)
{
	m_ElementSize = Point::size;
	m_Capacity = 0;
	m_RenderFlags = RENDER_TRANSPARENT;
	m_Identity = szName; 
	pVertexBuffer = NULL; 
	setTexture(pTexture, 0);
}

void D3DPointSpriteGroup::setTexture(Texture* pTexture, const unsigned int unit)
{
	if(unit == 0)
		m_pTextureUnitA = pTexture;
}

Texture* D3DPointSpriteGroup::getTexture(const unsigned int idx)
{
	return (idx == 0) ? m_pTextureUnitA : NULL;
}

HRESULT D3DPointSpriteGroup::PreRender()
{
	/*const unsigned int pointCount = m_Sprites.Count();

	if(pointCount == 0)
		return E_FAIL;

	if(pointCount > m_Capacity)
		ReallocateVertexBuffer(pointCount); 

	const LPDIRECT3DVERTEXBUFFER9 pVB = GetVertexBuffer(); 
	const unsigned int bufferSize = Point::size * pointCount; */
	Texture* pTexture = getTexture(0);/*
	void *pTmpVertices = NULL; 

	if(FAILED(pVB->Lock(0, bufferSize, (void**)&pTmpVertices, 0)))
		return E_FAIL;	
	memcpy(pTmpVertices, &m_Sprites[0], bufferSize); 
	if(FAILED(pVB->Unlock()))
		return E_FAIL;

	
	GetDevice()->GetDevice()->SetStreamSource(0, pVB, 0, Point::size); */
	GetDevice()->SetTexture(0,pTexture); 
	return S_OK;			
}

HRESULT D3DPointSpriteGroup::PostRender()
{
	return S_OK;
}
unsigned int D3DPointSpriteGroup::getVertexCount() const
{
	return m_Sprites.Count();
}
void D3DPointSpriteGroup::Render(const unsigned int vbOffset)
{
	GetDevice()->GetDevice()->DrawPrimitive(D3DPT_POINTLIST, vbOffset, m_Sprites.Count());
}

LPDIRECT3DVERTEXBUFFER9 D3DPointSpriteGroup::GetVertexBuffer()
{
	return pVertexBuffer; 
}

HRESULT D3DPointSpriteGroup::addPointsprite(D3DSprite **pInOut)
{
	unsigned int currentCapacity	= 0; 
	unsigned int currentSize		= 0; 
	LPDIRECT3DVERTEXBUFFER9 pVB		= NULL; 
	D3DSprite* pD3DSprite				= NULL; 

	try{
		if(ISNULL(pInOut) || ISNULL(pInOut))
			throw EE_EXCEPTION("Bad parameters"); 

		currentCapacity	= m_Capacity; 
		pVB	= GetVertexBuffer(); 

		pD3DSprite = new D3DSprite(GetDevice()); 
		pD3DSprite->m_pGroup = (D3DPointSpriteGroup*)this; 
		pD3DSprite->m_Index = m_RegisteredSprites.Count(); 
		m_RegisteredSprites.Add((D3DSprite*)pD3DSprite); 
		pD3DSprite->setColor(Color3f(1.0f,1.0f,1.0f)); 

		if(currentSize >= currentCapacity || ISNULL(pVB)) {
			
			currentCapacity = currentSize + 10; 

			ReallocateVertexBuffer(currentCapacity); 
			pVB	= GetVertexBuffer(); 
			
			void *pTmpVertices = NULL; 
			if(FAILED(pVB->Lock(0, m_ElementSize * currentCapacity, (void **) &pTmpVertices, 0)))
				throw EE_EXCEPTION("Lock() failed."); 
			EE_ZEROMEMORY(pTmpVertices, currentCapacity * m_ElementSize); 
			if(FAILED(pVB->Unlock()))
				throw EE_EXCEPTION("Unlock() failed."); 
		}
		else {
			
			void	*pTmpVertices = NULL; 
			if(FAILED(pVB->Lock(0, m_ElementSize * currentSize, (void **) &pTmpVertices, 0)))
				throw EE_EXCEPTION("Lock() failed."); 
			EE_ZEROMEMORY(pTmpVertices, currentSize * m_ElementSize); 
			if(FAILED(pVB->Unlock()))
				throw EE_EXCEPTION("Unlock() failed."); 
		}

		*pInOut = (D3DSprite*)pD3DSprite; 


		return S_OK; 

	}catch(CError::CErrorData errorData)
	{
		*pInOut = NULL; 
		
		if(pD3DSprite) pD3DSprite->Release(); 
		EE_SAFEDELETE(pD3DSprite); 

		if(pVB) pVB->Release(); 

		CError::CError(errorData); 

		return E_FAIL; 
	}
}

void* D3DPointSpriteGroup::getVertices()
{
	return &m_Sprites[0];

}

void D3DPointSpriteGroup::ReallocateVertexBuffer(unsigned int uiCount)
{
	if(pVertexBuffer)
	{
		if(FAILED(pVertexBuffer->Release()))
			throw EE_EXCEPTION("Failed to Release vertex buffer."); 
		pVertexBuffer = NULL; 
	}

	if(uiCount > 0)
	{
		m_Capacity = uiCount; 
		if(FAILED(GetDevice()->m_pD3DDevice->CreateVertexBuffer(m_Capacity * m_ElementSize,D3DUSAGE_WRITEONLY,
			0,D3DPOOL_DEFAULT,&pVertexBuffer,NULL))) 
			throw EE_EXCEPTION("CreateVertexBuffer() failed."); 
	}
}

HRESULT	D3DPointSpriteGroup::Release(void)
{
	m_IsValid = false; 
	m_Sprites.Empty(); 
	m_RegisteredSprites.Empty(); 

	if(pVertexBuffer) {
		if(FAILED(pVertexBuffer->Release()))
			return E_FAIL; 
		pVertexBuffer = NULL; 
	}
			
	return S_OK; 
}